package com.recSys.dataPrediction;

import de.bwaldvogel.liblinear.Feature;
import de.bwaldvogel.liblinear.FeatureNode;
import de.bwaldvogel.liblinear.Linear;
import de.bwaldvogel.liblinear.Model;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;

public class LinearPredictor {

    private static boolean  flag_predict_probability = true;

    private static final Pattern COLON = Pattern.compile(":");

    /**
     * <p><b>Note: The streams are NOT closed</b></p>
     */
    static public String doPredict(Model model, String line) throws IOException {


        int nr_class = model.getNrClass();
        double[] prob_estimates = null;
        int n;
        int nr_feature = model.getNrFeature();
        if (model.getBias() >= 0)
            n = nr_feature + 1;
        else
            n = nr_feature;

        if (flag_predict_probability && !model.isProbabilityModel()) {
            throw new IllegalArgumentException("probability output is only supported for logistic regression");
        }

        if (flag_predict_probability) {
            prob_estimates = new double[nr_class];
        }

        List<Feature> x = new ArrayList<Feature>();
        StringTokenizer st = new StringTokenizer(line, " \t\n");


        while (st.hasMoreTokens()) {
            String[] split = COLON.split(st.nextToken(), 2);
            if (split == null || split.length < 2) {
                throw new RuntimeException("Wrong input format at line "+line);
            }

            try {
                int idx = atoi(split[0]);
                double val = atof(split[1]);

                // feature indices larger than those in training are not used
                if (idx <= nr_feature) {
                    Feature node = new FeatureNode(idx, val);
                    x.add(node);
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("Wrong input format at line " + line, e);
            }
        }

        if (model.getBias() >= 0) {
            Feature node = new FeatureNode(n, model.getBias());
            x.add(node);
        }

        Feature[] nodes = new Feature[x.size()];
        nodes = x.toArray(nodes);

        double predict_label;
        String res="";
        if (flag_predict_probability) {
            int[] labels = model.getLabels();
            assert prob_estimates != null;
            predict_label = Linear.predictProbability(model, nodes, prob_estimates);

            // System.out.printf("%g", predict_label);
            for (int j = 0; j < model.getNrClass(); j++)
            {
                res =res+ labels[j]+":"+prob_estimates[j]+";";
                //System.out.printf(" %g", prob_estimates[j]);
            }


        } else {
            predict_label = Linear.predict(model, nodes);
        }

        return res;

    }

    static double atof(String s) {
        if(s != null && s.length() >= 1) {
            double d = Double.parseDouble(s);
            if(!Double.isNaN(d) && !Double.isInfinite(d)) {
                return d;
            } else {
                throw new IllegalArgumentException("NaN or Infinity in input: " + s);
            }
        } else {
            throw new IllegalArgumentException("Can't convert empty string to integer");
        }
    }

    static int atoi(String s) throws NumberFormatException {
        if(s != null && s.length() >= 1) {
            if(s.charAt(0) == 43) {
                s = s.substring(1);
            }

            return Integer.parseInt(s);
        } else {
            throw new IllegalArgumentException("Can't convert empty string to integer");
        }
    }

}